﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;

namespace BoardController.Helper
{
    public class AsyncServer
    {
        private Msg msg = new Msg();
        public string SENDMSG = "";
        public string RECVMSG = "";
        public Action<Msg> UpdateDevice;

        private int _maxClient;
        private int _clientCount;
        private Socket _serverSock;

        //private List<Session> _clientSessions;
        private Session ClientSession;
        #region Properties
        public bool IsRunning { get; private set; }

        public IPAddress Address { get; private set; }

        public int Port { get; private set; }

        public Encoding Encoding { get; set; }

        public int ClientCount
        {
            get
            {
                return _clientCount;
            }
        }

        public string[] LatestMsg
        {
            get
            {
                return new string[] { SENDMSG, RECVMSG };
            }
        }

        #endregion

        #region Basics

        /// <summary>
        /// 异步Socket TCP服务器
        /// </summary>
        /// <param name="localIPAddress">监听的IP地址</param>
        /// <param name="listenPort">监听的端口</param>
        /// <param name="maxClient">最大客户端数量</param>
        public AsyncServer(IPAddress localIPAddress, int listenPort, int maxClient)
        {
            this.Address = localIPAddress;
            this.Port = listenPort;
            this.Encoding = Encoding.Default;

            _maxClient = maxClient;
            //_clientSessions = new List<Session>();
            _serverSock = new Socket(localIPAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
            int a = 0;
        }

        public AsyncServer Start()
        {
            if (!IsRunning)
            {
                IsRunning = true;
                _serverSock.Bind(new IPEndPoint(this.Address, this.Port));
                _serverSock.Listen(1024);
                _serverSock.BeginAccept(new AsyncCallback(HandleAcceptConnected), _serverSock);
            }
            return this;
        }

        public AsyncServer Stop()
        {
            if (IsRunning)
            {
                IsRunning = false;
                _serverSock.Close();
                CloseAllSession();

                //TODO 关闭对所有客户端的连接

            }
            return this;
        }

        //public void CloseSingleSession(Session session)
        //{
        //    if (session != null)
        //    {
        //        session.Datagram = null;
        //        session.RecvDataBuffer = null;

        //        _clientSessions.Remove(session);
        //        _clientCount--;
        //        //ClientSession = null;
        //        //TODO 触发关闭事件
        //        session.Close();
        //    }
        //}

        public void CloseAllSession()
        {
            if (ClientSession == null)
                return;
            ClientSession.Datagram = null;
            ClientSession.RecvDataBuffer = null;
            _clientCount = 0;
            ClientSession.Close();
            //foreach (Session clientsession in _clientSessions)
            //{
            //    CloseSingleSession(clientsession);
            //}
            //_clientCount = 0;
            //_clientSessions.Clear();

        }
        #endregion

        #region ReceiveANDSend

        private void HandleAcceptConnected(IAsyncResult ar)
        {
            if (IsRunning)
            {
                try
                {
                    Socket server = (Socket)ar.AsyncState;
                    Socket client = server.EndAccept(ar);

                    //检查是否达到最大的允许的客户端数目
                    if (_clientCount > 1)
                    {
                        //TODO 触发事件
                        RaiseServerException(null);
                    }
                    else
                    {
                        CloseAllSession();
                        ClientSession = new Session(client);
                        _clientCount++;

                        Console.WriteLine("connected !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + DateTime.Now.ToLongTimeString());
                        //lock (_clientSessions)
                        //{
                        //    _clientSessions.Add(session);
                        //    _clientCount++;
                        //    RaiseClientConnected(session); //触发客户端连接事件
                        //}
                        ClientSession.RecvDataBuffer = new byte[client.ReceiveBufferSize];
                        //开始接受来自该客户端的数据

                        //try
                        //{
                        //    client.BeginReceive(ClientSession.RecvDataBuffer, 0, ClientSession.RecvDataBuffer.Length, SocketFlags.None,
                        // new AsyncCallback(HandleDataReceived), ClientSession);
                        //}
                        //catch (Exception e)
                        //{
                        //    Console.WriteLine(e.Message);
                        //}

                        client.BeginReceive(ClientSession.RecvDataBuffer, 0, ClientSession.RecvDataBuffer.Length, SocketFlags.None,
                             new AsyncCallback(HandleDataReceived), ClientSession);
                    }
                    //接受下一个请求
                    server.BeginAccept(new AsyncCallback(HandleAcceptConnected), ar.AsyncState);
                }
                catch (Exception ex)
                { }
            }
        }

        private void HandleDataReceived(IAsyncResult ar)
        {
            if (IsRunning)
            {
                Session session = (Session)ar.AsyncState;
                Socket client = session.ClientSocket;
                try
                {
                    //如果两次开始了异步的接收,所以当客户端退出的时候
                    //会两次执行EndReceive
                    int recv = client.EndReceive(ar);
                    if (recv == 0)
                    {
                        //TODO 触发事件 (关闭客户端)
                        //CloseSingleSession(session);
                        CloseAllSession();
                        RaiseNetError(session);
                        return;
                    }
                    else
                    {
                        byte[] data = new byte[recv];
                        Array.Copy(session.RecvDataBuffer, 0, data, 0, recv);
                        session.RecvData = data;
                    }

                    //TODO 处理已经读取的数据 ps:数据在session的RecvDataBuffer中
                    RaiseDataReceived(session);
                    //TODO 触发数据接收事件
                }
                catch (SocketException ex)
                {
                    //TODO 异常处理
                    RaiseNetError(session);
                }
                finally
                {
                    session.RecvDataBuffer = new byte[65536];
                    //try
                    //{
                    //    //继续接收来自来客户端的数据
                    //    client.BeginReceive(session.RecvDataBuffer, 0, session.RecvDataBuffer.Length, SocketFlags.None,
                    //     new AsyncCallback(HandleDataReceived), session);
                    //}
                    //catch(Exception e)
                    //{
                    //    Console.WriteLine(e.Message);
                    //}
                    client.BeginReceive(session.RecvDataBuffer, 0, session.RecvDataBuffer.Length, SocketFlags.None,
                         new AsyncCallback(HandleDataReceived), session);
                }
            }
        }

        public void Send(int addrDev, int function, int addrReg, int RegsCount, List<int> data, int label)
        {
            if (ClientSession != null)
            {
                Socket client = ClientSession.ClientSocket;
                if (!IsRunning)
                    throw new InvalidProgramException("This TCP Scoket server has not been started.");

                if (client == null)
                    throw new ArgumentNullException("client");

                if (data == null)
                    throw new ArgumentNullException("data");

                byte ByteAddrDev = (byte)addrDev;
                byte ByteFunction = (byte)function;
                byte[] ByteAddrRegs = { (byte)((addrReg >> 8) & 0xff), (byte)(addrReg & 0xff) };
                byte[] ByteCounts = { (byte)((RegsCount >> 8) & 0xff), (byte)(RegsCount & 0xff) };
                List<byte> ByteData = new List<byte>();

                foreach (var i in data)
                {
                    ByteData.Add((byte)((i >> 8) & 0xff));
                    ByteData.Add((byte)(i & 0xff));
                }

                List<byte> LabelData = new List<byte>();

                //LabelData.Add((byte)((label >> 24) & 0xff));
                //LabelData.Add((byte)((label >> 16) & 0xff));
                LabelData.Add((byte)((label >> 8) & 0xff));
                LabelData.Add((byte)(label & 0xff));

                var msg = CreateMsg(ByteAddrDev, ByteFunction, ByteAddrRegs, ByteCounts, ByteData, LabelData);
                string tmp = "";
                foreach (var item in msg)
                {
                    tmp += (item + "--");
                }
                Console.WriteLine(tmp);
                client.BeginSend(msg, 0, msg.Length, SocketFlags.None, new AsyncCallback(SendDataEnd), client);
            }
            else
            {

            }

        }

        private void SendDataEnd(IAsyncResult ar)
        {
            try
            {
                ((Socket)ar.AsyncState).EndSend(ar);
            }
            catch (Exception ex)
            { }
        }
        #endregion

        #region Events
        public delegate void NetHandle(object sender, AsyncEventArgs e);

        /// <summary>
        /// 接收到数据事件
        /// </summary>
        public event NetHandle DataReceived;

        private void RaiseDataReceived(Session session)
        {
            byte[] recvBytes = session.RecvData;

            if (recvBytes == null || recvBytes.Length <= 0)
            {
                return;
            }

            byte[] expectedList = { 0xAA, 0x55, 0xCC };
            int offset = 0;
            while (offset < recvBytes.Length)
            {
                switch (msg.curState.Name)
                {
                    case StateInfo.HEAD:

                        int index = 3 - msg.curState.Count;
                        byte expected = expectedList[index];
                        if (recvBytes[offset] == expected)
                        {
                            msg.Head[index] = expected;
                            msg.curState.Count -= 1;
                            //offset += 1;
                        }
                        offset += 1;
                        break;
                    case StateInfo.DEVICE:
                        msg.Device = recvBytes[offset];
                        msg.curState.Count -= 1;
                        offset += 1;
                        break;
                    case StateInfo.FUNCTION:
                        msg.Function = recvBytes[offset];
                        msg.curState.Count -= 1;
                        offset += 1;
                        break;
                    case StateInfo.REGISTER:
                        msg.Register[2 - msg.curState.Count] = recvBytes[offset];
                        msg.curState.Count -= 1;
                        offset += 1;
                        break;
                    case StateInfo.REGCOUNT:
                        msg.RegCount[2 - msg.curState.Count] = recvBytes[offset];
                        msg.curState.Count -= 1;
                        offset += 1;
                        break;
                    case StateInfo.DATA:
                        msg.Data.Add(recvBytes[offset]);
                        msg.curState.Count -= 1;
                        offset += 1;
                        break;
                    case StateInfo.LABEL:
                        msg.Label[2 - msg.curState.Count] = recvBytes[offset];
                        msg.curState.Count -= 1;
                        offset += 1;
                        break;
                    case StateInfo.CRC:
                        msg.CRC[2 - msg.curState.Count] = recvBytes[offset];
                        msg.curState.Count -= 1;
                        offset += 1;
                        if (msg.curState.Count == 0)
                        {
                            var crc = ComputeCRC(msg.GetList());
                            if (crc[0] == msg.CRC[0] && crc[1] == msg.CRC[1])
                            {
                                RECVMSG = "Done!!!  " + msg.GetList().Count.ToString() + "   " + ByteArrayToString(msg.GetList().ToArray());
                                if (msg.Function == 0x03)
                                {
                                    UpdateDevice(msg);
                                }

                                //Console.WriteLine(recvBytes + DateTime.Now.ToString("HH:mm:ss,fff"));
                            }
                            else
                            {
                                RECVMSG = "Fail!!!  " + msg.GetList().Count.ToString() + "   " + ByteArrayToString(msg.GetList().ToArray());
                            }

                            msg = new Msg();
                        }
                        break;
                    default:
                        //Console.WriteLine("Warning message");
                        break;
                }

                msg.curState.Next((msg.RegCount[0] * 256 + msg.RegCount[1]) * 2);
            }
        }

        /// <summary>
        /// 与客户端的连接已建立事件
        /// </summary>
        public event NetHandle ClientConnected;
        private void RaiseClientConnected(Session session)
        {
            if (ClientConnected != null)
            {
                ClientConnected(this, new AsyncEventArgs(session));
            }
        }

        /// <summary>
        /// 与客户端的连接已断开事件
        /// </summary>
        public event NetHandle ClientDisconnected;
        private void RaiseClientDisconnected(Socket client)
        {
            if (ClientDisconnected != null)
            {
                ClientDisconnected(this, new AsyncEventArgs("连接断开"));
            }
        }

        /// <summary>
        /// 触发网络错误事件
        /// </summary>
        public event NetHandle NetError;
        private void RaiseNetError(Session session)
        {
            if (NetError != null)
            {
                NetError(this, new AsyncEventArgs(session));
            }
        }


        /// <summary>
        /// 异常事件
        /// </summary>
        public event NetHandle ServerException;
        private void RaiseServerException(Session session)
        {
            if (ServerException != null)
            {
                ServerException(this, new AsyncEventArgs(session));
            }
        }
        #endregion

        private string ByteArrayToString(byte[] ba)
        {
            StringBuilder hex = new StringBuilder(ba.Length * 2);
            foreach (byte b in ba)
            {
                hex.AppendFormat("{0:x2}", b);
                hex.Append(" ");
            }

            return hex.ToString();
        }



        private byte[] CreateMsg(byte addrDev, byte function, byte[] addrRegs, byte[] counts, List<byte> data, List<byte> labels)
        {
            List<byte> msg = new List<byte>();
            msg.Add(0xAA);
            msg.Add(0x55);
            msg.Add(0xCC);

            msg.Add(addrDev);
            msg.Add(function);
            foreach (var b in addrRegs)
            {
                msg.Add(b);
            }

            foreach (var b in counts)
            {
                msg.Add(b);
            }

            foreach (var b in data)
            {
                msg.Add(b);
            }

            foreach (var l in labels)
            {
                msg.Add(l);
            }

            var crc = ComputeCRC(msg);
            msg.Add(crc[0]);
            msg.Add(crc[1]);

            return msg.ToArray();
        }

        public byte[] ComputeCRC(List<byte> datalist)
        {
            byte[] table = datalist.ToArray();
            const ushort polynomial = 0xA001;

            byte i, chChar;
            ushort wCRC = 0xFFFF;

            int j = 3;                     // it should start from head instead.
            while (j < table.Length)
            {
                chChar = table[j];
                wCRC ^= chChar;
                for (i = 0; i < 8; i++)
                {
                    if ((wCRC & 0x0001) != 0)
                        wCRC = (ushort)((wCRC >> 1) ^ polynomial);
                    else
                        wCRC >>= 1;
                }

                j++;
            }

            byte[] temp = new byte[2];
            temp[0] = (byte)wCRC;
            temp[1] = (byte)(wCRC >> 8);
            return temp;
        }
    }
}
